package jpaperwork.meta.transformer;

import jpaperwork.descriptor.EmbedForm;
import jpaperwork.descriptor.ReferenceData;
import jpaperwork.meta.descriptor.*;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;

/**
 * User: fleipold
 * Date: Jul 1, 2009
 * Time: 11:16:54 PM
 */
public class BeanFormReflector {
    public Form reflectClass(Class formClass){
        return (Form) reflectClassInternal(formClass);

    }
     private Type reflectClassInternal(Class formClass){
        if (formClass.isInterface()){
            return reflectInterface(formClass);
        }

        Form form = new Form();
        form.addDecoration(new ClassFormDecoration(formClass));
        for (Field field : formClass.getDeclaredFields()){
            if (isIgnored(field)){
                continue;
            }

            final Type type;

            if (field.getAnnotation(EmbedForm.class)!=null){
                if (List.class.isAssignableFrom(field.getType())){
                    
                    final ParameterizedType genericType = (ParameterizedType) field.getGenericType();
                    type = new Aggregation(reflectClassInternal((Class) genericType.getActualTypeArguments()[0]));

                } else {
                    type = reflectClassInternal(field.getType());
                }

            } else {            	
                type = new Flat(field.getType().isArray());
            }
            form.addField(new jpaperwork.meta.descriptor.Field(field.getName(), type));

        }
      return form;
    }

    private boolean isIgnored(Field field) {
        return field.getName().startsWith("$")
                || field.getName().startsWith("_")
                || Modifier.isStatic(field.getModifiers())
                || field.getName().equals("serialVersionUID")
                || field.getAnnotation(ReferenceData.class) != null;
    }

    private Type reflectInterface(Class formClass) {
        List <DiscriminatedType> types = new ArrayList<DiscriminatedType>();
        Subtypes subtypes = (Subtypes) formClass.getAnnotation(Subtypes.class);
        for (Class subtype : subtypes.value()){
            types.add(new DiscriminatedType(subtype.getSimpleName(), (Form) reflectClassInternal(subtype)));

        }
        return new Polymorphism(types);
    }
}
